M2.875 · Deep Learning · PEC3

2021-2 · Máster universitario en Ciencia de datos (Data science)

Estudios de Informática, Multimedia y Telecomunicación

 

PEC 3: Recurrent Neural Networks

En esta práctica implementaremos redes neuronales recurrentes para generar música.

Importante: La entrega debe hacerse en formato notebook y en formato html donde se vea el código y los resultados y comentarios de cada ejercicio. Para exportar el notebook a html puede hacerse desde el menú File $\to$ Download as $\to$ HTML.

0. Contexto y referencias

Esta PEC está basada en el siguiente artículo de investigación, aunque por motivos de extensión no podremos pasar por todos los puntos del artículo.

La primera etapa de esta PEC será leer y entender la idea general de este trabajo. El artículo os da acceso al código original con el que se ha llevado a cabo el trabajo y en el que además esta basado gran parte del código que aquí os adjuntamos, aunque por la complejidad y extensión del código no os recomendamos que sumergáis demasiado en él.

La PEC consta de una etapa de interpretación de los datos (un paso vital en todo proyecto real), donde se pedirá reproducir una gráfica del artículo. Luego entrenaremos una red LSTM para generar acordes a partir de archivos MIDI. Finalmente, usaremos la capa de embedding de la red entrenada para hacer una proyección de los acordes en 2D y visualizar el concepto de word2vec.

Además de este fichero os hemos adjuntado un archivo comprimido con una estructura de datos similar a la usada en el artículo y que os recomendamos (por vuestro bien) que no modifiquéis :). Allí encontraréis una carpeta llamada data donde se encuentra la base de datos que usaremos y otros ficheros que os facilitamos con datos de redes ya entrenadas (en la carpeta models)

1. Procesado de datos [2.5 pts]

1.1 Importación de módulos y paquetes necesarios

importamos módulos propios (archivos .py) que podréis encontrar en la estructura de carpetas que os hemos facilitado

1.2 Ficheros MIDI [1 pt]

En esta sección vamos a entender un poco mejor la información que contiene un fichero MIDI y reproduciremos un archivo de nuestra base de datos. Para esta sección os recomendamos que le echéis un vistazo a la sección "Extract notes" del siguiente tutorial de tensorflow.

1.2.1 Escoger un fichero MIDI de la base de datos y crear las funciones necesarias para extraer las notas, el tiempo en que se toca cada nota, su duración y el momento en que se tocan. Este proceso se debe aplicar solo a un instrumento: "Acoustic Grand Piano".

nota: Si el fichero escogido no tiene piano elegir otro fichero

1.2.2 Modificar la funcion plot_piano_roll del tutorial para poder graficar el pianoroll de la canción MIDI escogida. En este caso deberemos poder indicar un tiempo máximo de la canción que queremos añadir en el pianoroll. Además, si pasamos un max_time=-1, se deberán graficar todas las notas de la canción

1.3 Procesado de datos [1.5 pts]

Ahora volvemos al artículo y aplicaremos el procesado de datos. Buscar dentro del fichero data_processing_functions.py la función que se encarga de llevar a cabo todo el procesado de datos y llamarla desde aquí

La función que realiza el procesado completo de los datos se llama do_all_steps() no lleva parámetros y lo que hace es ejecutar una a una todas las funciones del fichero de data_processing_functions.py sobre el directorio de data donde se encuentran los ficheros tipo MDI

1.3.1 ¿Qué hace la función change_tempo_folder?¿Qué bpm fija para las canciones?

La función change_tempo_folder lo que hace es recorrer todos los subdirectorios de una carpeta source, para cada fichero cambia el nombre del path y genera un path objetivo dentro de la carpeta tempo, si este directorio target no existe, lo creará y posteriormente ejecutará el método change_tempo. La función básicamente lo que hace es migrar una serie de ficheros entre directorios.

1.3.2 Una vez tenemos ejecutado el procesado de datos se habrán creado muchas carpetas y muchos ficheros nuevos en la actual carpeta data. Ahora usaremos algunos de estos ficheros para construir la figura 2 del artículo usando los datos procesados. Crearemos dos funciones:

basadas en las funciones save_histo_oct_from_midi_folder y midi_to_histo_oct que encontraréis en los ficheros midi_functions.py y data_processing_functions.py. Ahora las modificaremos para que nos devuelvan datos para dos tipos de histogramas: uno igual a la figura 2 del artículo (apariciones VS pitch) y otro, usando el histo_oct, que serán datos para graficar apariciones VS notas en cualquier octava, es decir, el número de veces que aparece una nota (sin importar a que octava pertenezca) a lo largo de una canción.

indicación: Es importante entender la estructura de las variables:

dentro de la funcion midi_to_histo_oct

1.3.3 Buscar en internet sobre las escalas musicales e indicar que escala es la que más aparece en todas las canciones analizadas. ¿Cuáles dirías que son las notas más importantes de la escala?

2. LSTM para acordes [5 pts]

El siguiente paso en el artículo es el entrenamiento de una red LSTM para la generación de acordes. Esta parte la haremos con un data set más pequeño y lo compararemos con resultados de entrenamientos previos que os faciliaremos. Primero cargamos la librerias que necesitaremos

2.1 Carga de datos [0.5 pts]

2.1.1 En la carpeta data encontraréis un fichero llamado dataset.pkl que contiene 11338 canciones de train y 3780 de test. Usando pickle cargar los datos y luego separar 2000 para train y 500 canciones para test en las variables:

crea además train_set_size y test_set_size

2.2 Modelo con una capa de Embedding [0.5 pts]

2.2.1 Ahora definiremos los parámetros de nuestra red LSTM usando los parámetros que dan en el artículo. El único cambio será en el tamaño de la red LSTM, en la que usaremos la mitad de la LSTM del artículo y número de epochs que usaremos 10 en nuestro caso

El siguiente código es para mantener la estructura de carpetas que usan en el artículo ya que hay mucho código que depende de esta estructura:

2.2.2 Finalmente definimos y compilamos la red con una capa de Embedding antes de la LSTM. Para este paso, acceder al código original del artículo y buscar en que fichero se entrena la LSTM copiar la estructura eliminando código que no es de nuestro interés

2.3 Entrenamiento [2 pts]

2.3.1 Siguiendo el código orginal del artículo donde se define la arquitectura de la LSTM para los acordes hay 3 funciones más:

que nos serán de ayuda para el entrenamiento, testeo y para guardar los datos sobre el modelo entrenado. Guardaremos el valor de la loss tanto para test como para train cada 500 canciones, por lo que tendremos más de un punto por epoch. Modifica, si es necesario, las funciones test() y train() para que los ficheros de total_test y total_train dejen constancia de la epoch a la que pertenecen, esto te ayudará al graficar los datos más adelante.

Añade, antes de cada función una cabecera explicando brevemente lo que ésta hace.


nota: comprueba que has definido todas las variables necesarias para que las funciones no den error en mitad del entrenamiento. Comienza usando menos datos para agilizar el proceso y una vez funcione todo usa el set completo

2.3.2 Responde las siguientes preguntas:

2.3.3 Entrena la red usando las funciones anteriores

El colab tuvo un error acá y no terminó el proceso de entrenamiento

2.4 Comparación de modelos [2 pts]

En este último paso compararemos diferentes modelos. En la carpeta models/chords encontrarás los datos (en un formato similar al generado anteriormente) del entrenamiento de modelos similares con otros parámetros:

  1. carpeta: 2717-ShiftedTrue...
  2. carpeta: 5102-ShiftedTrue...

2.4.1 Compara los 3 modelos: grafica las funciones de loss para train y test de cada modelo.

2717-Shifted_True_Lr

5102-Shifted_True_Lr

Los modelos datos difieren en el número de épocas y en los tamaños de los datasets de train y test. La estructura de la red es la misma en los casos presentados

Comparación de la pérdida de los modelos

Los modelos muestran overfitting debido a las diferencias en la pérdida que se observan entre el dataset de train y test

En el caso del modelo propio no se observa aún overfitting sin embargo el entrenamiento no se completó

3 Análisis de la capa de embedding [2 pts]

En esta sección recrearemos la figura 8 del artículo con la red que hemos entrenado y la que mejor performance nos ha dado en la comparación.

3.1 Carga de modelos entrenados y generación de embeddings [1 pts]

3.1.1 Utiliza el load_model de keras para cargar:

Se elegió el 5102-Shifted por tener una curva de pérdida en train y test un poco más suavizada y con la forma esperada

3.1.2 Ahora crearemos, para cada modelo, un nuevo modelo para los embedding. Para hacer esto, deberás crear un nuevo modelo cuya entrada sea igual a la entrada de los modelos originales y la salida sea el layer "embedding". Esto lo puedes hacer usando la función get_layer() del modelo.

3.1.3 El modelo de embedding recibe un acorde y devuelve un vector de 10 dimensiones con el embedding para cada acorde. Calcula estos vectores con cada uno de los modelos de embedding

3.1.4 Aplica un squeeze para eliminar los ejes de dimensión 1. El resultado de este squeeze debería ser (50,10)

Se repite el proceso para nuestro modelo, utilizaremos la información del último fichero pickle

3.2 Análisis PCA [1 pts]

Para acabar con nuestro análisis de los embeddings, vamos a realizar un proceso de PCA para reducir las dimensiones de los vectores resultantes y poder graficarlos en 2D.

3.2.1 Escalar los vectores y aplicar PCA para reducir a 2 las dimensiones

3.2.2 Grafica para ambos embeddings los vectores en un scatterplot. Añadele anotaciones con las notas del acorde y comenta el resultado: ¿Observas el efecto word2vect en las gráficas?

Usa esta función para obtener un índice para los acordes y sus notas

Realizamos el mismo proceso pero con las predicciones generadas por nuestro modelo

En los gráficos de dispersión de las predicciones de ambos modelos se observan agrupaciones de notas, por lo tanto si hay un efecto de la capa embeddig

3.2.3 Crea una función para calcular todas las distancias entre los primero 20 acordes. Luego haz una lista con las 10 distancias mínimas para ambos modelos. ¿Están relacionados estos acordes con distancias mínimas?

4. Rueda de 5tas (Bonus Track) [0.5 pts]

En este bonus track, y usando lo mismo que has usado para los apartados anteriores, podrás dibujar la rueda de quintas con datos entrenados sobre la misma base de datos, pero no hacer el shifteo de los acordes.

Usa los datos que encontrarás en la carpeta models/chords/9671-Shifted_False... para repetir el proceso del análisis de PCA

Una vez tienes los vectores, llama a las siguientes funciones para dibujar la rueda de quintas